package com.autumn.platform.core.spring;

import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.aopalliance.aop.Advice;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.config.CacheManagementConfigUtils;
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.SimpleCacheResolver;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.AbstractResourceBasedMessageSource;
import org.springframework.context.support.DelegatingMessageSource;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;

import com.autumn.platform.core.Defaults;
import com.autumn.platform.core.spring.ResourceWrapperPatternResolver.ResoruceWrapper;

public abstract class SpringHolder {

    private SpringHolder() {};

    private static final String AUTUMN_MESSAGE_BASE_NAME = "classpath*:META-INF/**/autumn*_zh_CN.properties";

    private static final AtomicBoolean monitor = new AtomicBoolean(false);

    private static ApplicationContext applicationContext;

    private static Environment environment;

    private static MessageSource messageSource;

    private static ResourcePatternResolver resourcePatternResolver;

    private static CacheManager cacheManager;

    private static ConversionService conversionService;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        if (null == applicationContext) {
            return;
        }
        SpringHolder.applicationContext = applicationContext;

        // 环境
        if (null != applicationContext.getEnvironment()) {
            environment = Defaults.getDefaultComponentIfNull(applicationContext.getEnvironment(), Environment.class);
        }
        // 国际化资源管理器
        messageSource = applicationContext;
        setDefaultMessageBasenames(messageSource);
        // 资源加载器
        resourcePatternResolver = applicationContext;
        // 缓存管理器
        initCacheManager(applicationContext);
        // 类型转换器
        initConversionService(applicationContext);
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static Environment getEnvironment() {
        return Defaults.getDefaultComponentIfNull(environment, Environment.class);
    }

    public static MessageSource getMessageSource() {
        MessageSource ms = Defaults.getDefaultComponentIfNull(messageSource, MessageSource.class);
        setDefaultMessageBasenames(ms);
        return ms;
    }

    public static String getMessage(String code, Object[] args) throws NoSuchMessageException {
        return getMessage(code, args, (Locale) null);
    }

    public static String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
        if (null == locale) {
            locale = LocaleContextHolder.getLocale();
        }
        return getMessageSource().getMessage(code, args, locale);
    }

    public static String getMessage(String code, Object[] args, String defaultMessage) {
        return getMessage(code, args, defaultMessage, null);
    }

    public static String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
        if (null == locale) {
            locale = LocaleContextHolder.getLocale();
        }
        return getMessageSource().getMessage(code, args, defaultMessage, locale);
    }

    public static ResourcePatternResolver getResourcePatternResolver() {
        return Defaults.getDefaultComponentIfNull(resourcePatternResolver, ResourcePatternResolver.class);
    }

    public static Resource getResource(String location) throws IOException {
        ResourcePatternResolver resolver = getResourcePatternResolver();
        Environment environment = getEnvironment();
        location = environment.resolveRequiredPlaceholders(location);
        return resolver.getResource(location);
    }

    public static Set<Resource> getResources(String... locationPatterns) throws IOException {
        ResourcePatternResolver resolver = getResourcePatternResolver();
        Environment environment = getEnvironment();
        Set<Resource> resources = new LinkedHashSet<Resource>();
        for (String location : locationPatterns) {
            location = environment.resolveRequiredPlaceholders(location);
            for (Resource resource : resolver.getResources(location)) {
                resources.add(resource);
            }
        }
        return resources;
    }

    public static CacheManager getCacheManager() {
        return Defaults.getDefaultComponentIfNull(cacheManager, CacheManager.class);
    }

    public static Cache getCache(String name) {
        return getCacheManager().getCache(name);
    }

    public static Collection<String> getCacheNames() {
        return getCacheManager().getCacheNames();
    }

    public static ConversionService getConversionService() {
        return Defaults.getDefaultComponentIfNull(conversionService, ConversionService.class);
    }

    public static <T> T convert(Object source, Class<T> targetType) {
        ConversionService service = getConversionService();
        return service.convert(source, targetType);
    }

    /**
     * 设置默认国际化消息资源文件
     * 
     * @param ms
     */
    private static void setDefaultMessageBasenames(MessageSource ms) {
        if (!monitor.get()) {
            synchronized (monitor) {
                if (!monitor.get()) {
                    try {
                        if (null != applicationContext) {
                            try {
                                ms = applicationContext.getBean(MessageSource.class);
                                if (ms instanceof DelegatingMessageSource) {
                                    ms = ((DelegatingMessageSource) ms).getParentMessageSource();
                                }
                            } catch (Exception e) {
                            }
                        }

                        if (ms instanceof AbstractResourceBasedMessageSource) {
                            AbstractResourceBasedMessageSource ams = (AbstractResourceBasedMessageSource) ms;
                            Set<String> basenames = getDefaultMessageBasenames();
                            if (null != basenames && !basenames.isEmpty()) {
                                ams.addBasenames(basenames.toArray(new String[0]));
                            }
                        }
                    } catch (Throwable e) {
                    } finally {
                        monitor.set(true);
                    }
                }
            }
        }
    }

    /**
     * 自动查找国际化文件
     * 
     * @return
     */
    public static Set<String> getDefaultMessageBasenames() {
        try {
            ResourceWrapperPatternResolver resourceResolver = new ResourceWrapperPatternResolver();
            Set<ResoruceWrapper> resources = resourceResolver.findPathMatchingResourcesWrapper(AUTUMN_MESSAGE_BASE_NAME);
            if (null != resources && !resources.isEmpty()) {
                Set<String> basenames = new HashSet<>();
                for (ResoruceWrapper resource : resources) {
                    String filename = resource.getRelativePath();
                    //去掉classpath*:(长度为11)，并去掉_zh_CN.properties（长度为17）的后缀
                    String basename = /* "classpath:" + */filename.substring(11, filename.length() - 17);
                    basenames.add(basename);
                }
                return basenames;
            }
        } catch (Throwable e) {
        }
        return null;
    }

    private static void initCacheManager(ApplicationContext applicationContext) {
        if (null == cacheManager) {
            try {
                CacheManager cm = applicationContext.getBean(CacheManager.class);
                if (null != cm) {
                    cacheManager = cm;
                    return;
                }
            } catch (Throwable e) {
            }

            try {
                // 缓存切面
                BeanFactoryCacheOperationSourceAdvisor bean = applicationContext.getBean(CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME, BeanFactoryCacheOperationSourceAdvisor.class);
                // 缓存通知
                Advice advice = bean.getAdvice();
                if (advice instanceof CacheInterceptor) {
                    CacheResolver cacheResolver = ((CacheInterceptor) advice).getCacheResolver();
                    if (cacheResolver instanceof SimpleCacheResolver) {
                        CacheManager cm = ((SimpleCacheResolver) cacheResolver).getCacheManager();
                        if (null != cm) {
                            cacheManager = cm;
                        }
                    }
                }
            } catch (Throwable e) {
            }
        }
    }

    private static void initConversionService(ApplicationContext applicationContext) {
        if (null == conversionService) {
            try {
                ConversionService cs = null;
                AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
                if (beanFactory instanceof ConfigurableBeanFactory) {
                    cs = ((ConfigurableBeanFactory) beanFactory).getConversionService();
                }
                if (null == cs) {
                    cs = applicationContext.getBean(ConversionService.class);
                }
                if (null != cs) {
                    conversionService = cs;
                }
            } catch (Throwable e) {
            }
        }
    }
}
